在談論完書中提的組織 domain 的三種模式 :
接下來我們要來提書中將他放在 domain 章節的『 Service 』。
先提一下,我前面有幾個範例裡有寫到 service,但嚴格來說這不是書中所提 service 層意思,之前範例是指商業邏輯用的地方,我範例都寫 service,應該不少人的定義也是相同,但書中不同。
從書中抓出的一張圖應該就可以知道, service 在書中實際上的位置如下,從下圖可以簡單的定義 :
Service Layer 會在 domain model 的前,但他還是屬於 domain 層,它負責處理業務的工作流處理。
我當初在讀 domain model 那章節我事實上一直有地方卡到,那就是 domain model 可以當做以每個業務為單位,然後內包屬性與行為,那假設有個業務為『 這個月當用戶註冊後,要送 Coupon 卷 』,這個業務要如何處理呢 ?
以 domain model 應該是會分 :
但問題是,同時呼叫這兩個方法的地方是那 ? 對 ~ 這個地方就是 『 Service Layer 』。
圖片來源: 《 企業應用架構模式 》
書中事實上還有提到幾個 Service Layer 的使用重點 :
我事實上在思考 ~ 有時後我們 Web 開發時,有分所謂的 controller、service 會不會嚴格來說,以書中定義 controller ⇒ service 而 service ⇒ domain model 呢 ? 因為我相信不少人 controller 裡面,是寫這個 api 要處理的事情 ~ 對吧 ?
不過我這裡不太建議,以我自已的標準這三個實際上該做的事情為 :
這裡給個簡單的範例,業務需求為 :
當用戶註冊時,會給一張 Coupon ( 折扣卷 ),且寄出歡迎信
簡單來說該業務可以分為三個部份 :
其中有幾點要注意 :
class ApplicationService {
getEmailGateWay(): IEmailGateway{
return new EmailGatewayService()
}
}
interface IEmailGateway{
sendEmailMessage(toEmail: string, subject: string, body: any): void
}
class EmailGatewayService implements IEmailGateway{
sendEmailMessage(toEmail: string, subject: string, body: any): void{
console.log('取得該 application 的寄信服務的 config,然後在寄信')
}
}
class UserService extends ApplicationService{
register(username: string, email: string): void{
const userDomainModel = new UserDomainModel({})
const couponDomainModel = new CouponDomainModel({})
// 建立使用者
const user = new User(username, email)
userDomainModel.create(user)
// 建立第一次註冊 Coupon
const firstCoupon = new Coupon('First Coupon', 100)
couponDomainModel.setCoupon(firstCoupon)
couponDomainModel.sendToUser(user.id)
// 寄歡迎信
this.getEmailGateWay().sendEmailMessage('mark@gmail.com', 'First Register', {})
}
}
class User{
username: string
email: string
constructor(username: string, email: string){
this.username = username
this.email= email
}
}
class Coupon{
title: string
discount: number
constructor(title: string, discount: number){
this.title = title
this.discount = discount
}
}
class UserDomainModel{
userDao: any
constructor(userDao: any){
this.userDao = userDao
}
create(user: User){
this.userDao.create(user)
}
}
class CouponDomainModel{
couponDao: any
coupon: Coupon
constructor(couponDao: any){
this.couponDao = couponDao
}
setCoupon(coupon){
this.coupon = coupon
}
sendToUser(userId){
console.log('Save to user coupon table')
this.couponDao.save(this.coupon, userId)
}
}
有時後在寫業務需求時,我發現真的很難每一個 domain 都定義清楚,像我剛剛提到的範例 :
當用戶註冊時,會給一張 Coupon ( 折扣卷 ),且寄出歡迎信
如果我們將『 用戶註冊時,會給一張 Coupon 』,寫在 User Domain Model 中,但如果其它 application 不需要給一張 coupon 時,要怎麼辦呢 ? 這時有了 service 專門處理工作流的地方真的方便不少。
事實上我寫到現在,單純的使用 domain model 來寫,我還真想不到如果寫,但多了一個 service layer 就真的方便多了…
然後在看《 搞笑談軟工-PoEEA之Server Layer 這篇文章 』》有提到一句話 :
傳統階層式架構所說的 Service Layer,就是Clean Architecture裡面的 Use Case Layer。Teddy現在覺得Use Case Layer比較具體,因為Service這個字有很多種含意,因此用Service Layer來代表應用程式所提供功能或服務的邊界,好像有點那麼不是很直覺
這句話我覺得有理,給其它人參考看看。